package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.jsola.cache.ICache;
import org.jsola.common.DateKit;
import org.jsola.common.DateKit8;
import org.jsola.common.StrKit;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.*;
import org.jsola.hr.constant.ExtraType;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.IAttendPunchRecordDAO;
import org.jsola.hr.dto.*;
import org.jsola.hr.dto.attendrule.AttendRuleLocationAddDTO;
import org.jsola.hr.dto.attendrule.AttendRulePunchTimeAddDTO;
import org.jsola.hr.dto.attendrule.AttendRuleWifiAddDTO;
import org.jsola.hr.dto.attendschedule.AttendPeriodDTO;
import org.jsola.hr.entity.*;
import org.jsola.hr.exception.HrException;
import org.jsola.hr.provider.IUserProviderService;
import org.jsola.hr.query.*;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.*;
import org.jsola.orm.filter.Order;
import org.jsola.user.core.TokenUser;
import org.jsola.user.core.UserContext;
import org.jsola.user.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wxz
 */
@Slf4j
@Service("hrAttendPunchServiceImpl")
public class AttendPunchServiceImpl implements IAttendPunchService {

    @Autowired
    private IEmpInfoService empInfoService;
    @Autowired
    private IAttendPunchStaffService attendPunchStaffService;
    @Autowired
    private IAttendPlanService attendPlanService;
    @Autowired
    private IAttendPunchRecordService attendPunchRecordService;
    @Autowired
    private IAttendPunchDailySummaryService attendPunchDailySummaryService;
    @Autowired
    private IAttendPunchDailyReportService attendPunchDailyReportService;
    @Autowired
    private IAttendPeopleMonthlyStatService attendPeopleMonthlyStatService;
    @Autowired
    private ICache cache;
    @Autowired
    private IEmpDepService empDepService;
    @Autowired
    private IAttendPunchCommonService attendPunchCommonService;
    @Autowired
    private IAttendGoOutService attendGoOutService;
    @Autowired
    private IAttendOvertimeRuleService attendOvertimeRuleService;
    @Autowired
    private IUserProviderService userProviderService;
    @Autowired
    private IDepInfoService depInfoService;
    @Autowired
    private IAttendPunchRecordDAO attendPunchRecordDAO;
    @Autowired
    private IAttendExtraJsonService attendExtraJsonService;

    /**
     * 检查考勤打卡
     *
     * @param attendPunchCheckQuery 检查考勤打卡参数
     * @param tokenUser             当前用户
     * @return
     */
    @Override
    public AttendPunchCheckVO checkPunch(AttendPunchCheckQuery attendPunchCheckQuery, TokenUser tokenUser) {

        // 当前的考勤日期
        Date now = new Date(System.currentTimeMillis());
        String attendDateStr = DateFormatUtils.format(now, "yyyy-MM-dd");
        // 打卡时间
        String punchTimeStr = DateFormatUtils.format(now, "HH:mm");
        // 查询员工信息
        EmpInfoDO empInfoDO = this.getEmpInfoDO(tokenUser);
        if (empInfoDO == null) {
            throw new HrException("未查询到员工信息");
        }
        // 检查考勤打卡 请求参数记录
        log.info("empInfoId:{},检查考勤打卡请求参数:{}", empInfoDO.getId(), JSON.toJSONString(attendPunchCheckQuery));
        // 是否已设置考勤规则
        Boolean isSetPlan = true;
        // 查询当前用户使用的考勤规则
        List<AttendPlanDO> attendPlanDOList = attendPlanService.getAttendPlan(empInfoDO.getId(), attendDateStr, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(attendPlanDOList)) {
//    		throw new HrException("未设置考勤规则");
            isSetPlan = false;
            AttendPunchCheckVO attendPunchCheckVO = new AttendPunchCheckVO();
            attendPunchCheckVO.setIsSetPlan(isSetPlan);
            return attendPunchCheckVO;
        }
        // 判断生效时间
        if (attendPlanDOList.get(0).getTakeEffect() != null
                && attendPlanDOList.get(0).getTakeEffect() != 2
                && attendPlanDOList.get(0).getStartEffectTime() != null
                && attendPlanDOList.get(0).getStartEffectTime().getTime() > System.currentTimeMillis()) {
            AttendPunchCheckVO attendPunchCheckVO = new AttendPunchCheckVO();
            attendPunchCheckVO.setIsSetPlan(false);
            return attendPunchCheckVO;
        }

        // 使用的考勤规则
        AttendPlanDO usedAttendPlan = null;

        // 是否在打卡范围内;1-是,0-否
        int rangeState = 0;
        String punchLocation = attendPunchCheckQuery.getPunchLocation();

        Boolean isWifiConfig = false;
        Boolean isWifiMatch = false;
        // 如果是WIFI打卡, 是否在打卡范围内
        if (StringUtils.isNotBlank(attendPunchCheckQuery.getMacAddr())
                && StringUtils.isNotBlank(attendPunchCheckQuery.getWifiName())) {
            log.info("WIFI打卡");
            isWifiConfig = true;
            // WIFI打卡
            usedAttendPlan = this.wifiPunch(attendPlanDOList, attendPunchCheckQuery.getWifiName(), attendPunchCheckQuery.getMacAddr());
            if (usedAttendPlan != null) {
                rangeState = 1;
                isWifiMatch = true;
            } else {
                log.info("未配置到WIFI");
            }
        }

        // 如果WIFI不在打卡范围内，则判断移动网络
        Double exceed = null;
        if (rangeState == 0) {
            log.info("如果WIFI不在打卡范围内，则判断移动网络");
            // 后台设置了打卡地点,判断是否在打卡范围内
            for (AttendPlanDO attendPlanDO : attendPlanDOList) {
                if (StringUtils.isNotBlank(attendPlanDO.getPunchLocationJson())) {
                    MobilePunchVO mobilePunchVO = this.mobilePunch(attendPunchCheckQuery.getLongitude(), attendPunchCheckQuery.getLatitude(), attendPlanDO);
                    if (mobilePunchVO.getAttendPlanDO() != null) {
                        rangeState = 1;
                        punchLocation = mobilePunchVO.getLocation();
                        usedAttendPlan = mobilePunchVO.getAttendPlanDO();
                        break;
                    } else {
                        exceed = mobilePunchVO.getMinDifference();
                        DecimalFormat df = new DecimalFormat("#0.0");
                        exceed = Double.parseDouble(df.format(exceed));
                    }
                }
            }
        }

        log.info("检查考勤打卡---> 员工ID：" + empInfoDO.getId() + " 员工姓名:" + empInfoDO.getName());
        // 如果不在打卡范围内且不允许范围外打卡
        if (rangeState == 0 && attendPlanDOList.get(0).getPunchRange() == 3) {
            log.info("不在打卡范围内");
            AttendPunchCheckVO attendPunchCheckVO = new AttendPunchCheckVO();
            attendPunchCheckVO.setRangeState(rangeState);
            attendPunchCheckVO.setPunchRange(3);
            attendPunchCheckVO.setExceed(exceed);
            return attendPunchCheckVO;
        } else {
            // 范围外打卡
            usedAttendPlan = attendPlanDOList.get(0);
            punchLocation = attendPunchCheckQuery.getPunchLocation();
        }

        // 如果是明日生效  生效时间 ; 1-明日生效，2-立即生效
//        if (usedAttendPlan.getTakeEffect() == 1) {
        // 并且当前的时间小于生效开始日期
//            if (System.currentTimeMillis() < usedAttendPlan.getStartEffectTime().getTime()) {
        // 根据临时考勤规则ID查询
//				AttendPlanDO attendPlanDO = attendPlanService.selectDOById(usedAttendPlan.getTmpAttendPlanId(), usedAttendPlan.getSiteId());
//				usedAttendPlan = attendPlanDO;
//            }
//        }

        log.info("检查考勤打卡---> 员工ID：" + empInfoDO.getId() + " 员工姓名:" + empInfoDO.getName() + " 使用的考勤规则ID:" + usedAttendPlan.getId() + " 名称:" + usedAttendPlan.getName());

        // 公共考勤打卡
        PunchCommonDTO punchCommonDTO = new PunchCommonDTO();

        punchCommonDTO.setAttendPlanId(usedAttendPlan.getId());
        punchCommonDTO.setPlanType(usedAttendPlan.getPlanType());
        punchCommonDTO.setEmpInfoId(empInfoDO.getId());
        punchCommonDTO.setAttendDateStr(attendDateStr);
        punchCommonDTO.setPunchTimeStr(punchTimeStr);
        punchCommonDTO.setIsSynchLegalHoliday(usedAttendPlan.getIsSynchLegalHoliday());

        PunchCommonVO punchCommonVO = attendPunchCommonService.punchCommon(punchCommonDTO, tokenUser);
//		// 异常打卡
//		if (punchCommonVO.getWorkStatus() != 0) {
//			throw new HrException("未到打卡时间");
//		}

        // 如果有考勤高级设置
        AttendAdvancedDTO advanced = punchCommonVO.getAdvanced();

        String punchNumber = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss") + HrKit.getRandom();
        AttendPunchCheckVO attendPunchCheckVO = punchCommonVO.to(AttendPunchCheckVO.class);
        // 打卡编号
        attendPunchCheckVO.setPunchNumber(punchNumber);
        // 是否已设置考勤规则
        attendPunchCheckVO.setIsSetPlan(isSetPlan);
        // 是否在打卡范围内;1-是,0-否
        attendPunchCheckVO.setRangeState(rangeState);
        // 公司ID
        attendPunchCheckVO.setCompanyId(usedAttendPlan.getCompanyId());
        // 公司名称
        attendPunchCheckVO.setCompanyName(usedAttendPlan.getCompanyName());
        // 考勤规则ID
        attendPunchCheckVO.setAttendPlanId(usedAttendPlan.getId());
        // 范围外打卡
        attendPunchCheckVO.setPunchRange(usedAttendPlan.getPunchRange());
        // 员工打卡时必须拍照
        attendPunchCheckVO.setIsTakePhoto(usedAttendPlan.getIsTakePhoto());
        // 只能拍照
        attendPunchCheckVO.setIsOnlyPhoto(usedAttendPlan.getIsOnlyPhoto());
        // 打卡地点
        attendPunchCheckVO.setPunchLocation(punchLocation);
        // 是否配置了wifi打卡
        attendPunchCheckVO.setIsWifiConfig(isWifiConfig);
        // 是否匹配到wifi打卡
        attendPunchCheckVO.setIsWifiMatch(isWifiMatch);
        // 如果是超过了打卡范围超过了多少米
        attendPunchCheckVO.setExceed(exceed);
        // 将打卡检查信息放入缓存（三分钟内有效）
        cache.set(CacheKeyKit.getAttendPunchCheckInfo(punchNumber), JSON.toJSONString(attendPunchCheckVO), (3 * 60 * 1000));

        return attendPunchCheckVO;
    }

    /**
     * 检查考勤打卡
     *
     * @param attendPunchCheckQuery 检查考勤打卡参数
     * @param tokenUser             当前用户
     * @return
     */
    @Override
    public AttendPunchCheckVO checkPunch2(AttendPunchCheckQuery attendPunchCheckQuery, TokenUser tokenUser) {

        // 当前的考勤日期
        String attendDateStr = DateFormatUtils.format(new Date(), "yyyy-MM-dd");
        // 打卡时间
        String punchTimeStr = DateFormatUtils.format(new Date(), "HH:mm");

        punchTimeStr = new SimpleDateFormat("HH:mm").format(randomDate("2019-01-01", "2019-01-31"));

        // 查询员工信息
        EmpInfoDO empInfoDO = this.getEmpInfoDO(tokenUser);
        // 查询当前用户使用的考勤规则
        List<AttendPlanDO> attendPlanDOList = attendPlanService.getAttendPlan(empInfoDO.getId(), attendDateStr, tokenUser.getSiteId());
        if (attendPlanDOList == null) {
            throw new HrException("未设置考勤规则");
        }
        // 使用的考勤规则
        AttendPlanDO usedAttendPlan = null;

        // 是否在打卡范围内;1-是,0-否
        int rangeState = 0;
        // 如果是WIFI打卡, 是否在打卡范围内
        String wifiName = "";
        if (StringUtils.isNotBlank(attendPunchCheckQuery.getMacAddr())
                && StringUtils.isNotBlank(attendPunchCheckQuery.getWifiName())) {

            // WIFI打卡
            usedAttendPlan = this.wifiPunch(attendPlanDOList, attendPunchCheckQuery.getWifiName(), attendPunchCheckQuery.getMacAddr());
            if (usedAttendPlan != null) {
                wifiName = attendPunchCheckQuery.getWifiName();
                rangeState = 1;
            }
        }

        // 如果WIFI不在打卡范围内，则判断移动网络
        String punchLocation = "";
        if (rangeState == 0) {
            // 后台设置了打卡地点
            for (AttendPlanDO attendPlanDO : attendPlanDOList) {
                if (StringUtils.isNotBlank(attendPlanDO.getPunchLocationJson())) {
                    MobilePunchVO mobilePunchVO = this.mobilePunch(attendPunchCheckQuery.getLongitude(), attendPunchCheckQuery.getLatitude(), attendPlanDO);
                    if (mobilePunchVO.getAttendPlanDO() != null) {
                        punchLocation = mobilePunchVO.getLocation();
                        usedAttendPlan = mobilePunchVO.getAttendPlanDO();
                        rangeState = 1;
                        break;
                    }
                }
            }
        }

        if (rangeState == 0) {
            AttendPunchCheckVO attendPunchCheckVO = new AttendPunchCheckVO();
            attendPunchCheckVO.setRangeState(rangeState);
            return attendPunchCheckVO;
        }

        // 如果是明日生效  生效时间 ; 1-明日生效，2-立即生效
        if (usedAttendPlan.getTakeEffect() == 1) {
            // 并且当前的时间小于生效开始日期
            if (System.currentTimeMillis() < usedAttendPlan.getStartEffectTime().getTime()) {
                // 根据临时考勤规则ID查询
//				AttendPlanDO attendPlanDO = attendPlanService.selectDOById(usedAttendPlan.getTmpAttendPlanId(), usedAttendPlan.getSiteId());
//				usedAttendPlan = attendPlanDO;
            }
        }

        // 公共考勤打卡
        PunchCommonDTO punchCommonDTO = new PunchCommonDTO();

        punchCommonDTO.setAttendPlanId(usedAttendPlan.getId());
        punchCommonDTO.setPlanType(usedAttendPlan.getPlanType());
        punchCommonDTO.setEmpInfoId(empInfoDO.getId());
        punchCommonDTO.setAttendDateStr(attendDateStr);
        punchCommonDTO.setPunchTimeStr(punchTimeStr);
        punchCommonDTO.setIsSynchLegalHoliday(usedAttendPlan.getIsSynchLegalHoliday());

        PunchCommonVO punchCommonVO = attendPunchCommonService.punchCommon(punchCommonDTO, tokenUser);
        // 异常打卡
//		if (punchCommonVO.getWorkStatus() != 0) {
//			throw new HrException("未到打卡时间");
//		}

        String punchNumber = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss") + HrKit.getRandom();
        AttendPunchCheckVO attendPunchCheckVO = punchCommonVO.to(AttendPunchCheckVO.class);
        // 打卡编号
        attendPunchCheckVO.setPunchNumber(punchNumber);
        // 是否在打卡范围内;1-是,0-否
        attendPunchCheckVO.setRangeState(rangeState);
        // 公司ID
        attendPunchCheckVO.setCompanyId(usedAttendPlan.getCompanyId());
        // 公司名称
        attendPunchCheckVO.setCompanyName(usedAttendPlan.getCompanyName());
        // 打卡地点
        attendPunchCheckVO.setPunchLocation("");
        // 考勤规则ID
        attendPunchCheckVO.setAttendPlanId(usedAttendPlan.getId());
        // 范围外打卡
        attendPunchCheckVO.setPunchRange(usedAttendPlan.getPunchRange());
        // 员工打卡时必须拍照
        attendPunchCheckVO.setIsTakePhoto(usedAttendPlan.getIsTakePhoto());
        // 只能拍照
        attendPunchCheckVO.setIsOnlyPhoto(usedAttendPlan.getIsOnlyPhoto());
        // 打卡wifi
        attendPunchCheckVO.setWifiName(wifiName);
        // 打卡地点
        attendPunchCheckVO.setPunchLocation(punchLocation);

        // 将打卡检查信息放入缓存
        cache.set(CacheKeyKit.getAttendPunchCheckInfo(punchNumber), JSON.toJSONString(attendPunchCheckVO), (20 * 60 * 1000));

        return attendPunchCheckVO;
    }

    /**
     * 移动网络打卡
     *
     * @param longitude
     * @param latitude
     * @param attendPlanDO
     * @return
     */
    private MobilePunchVO mobilePunch(String longitude, String latitude, AttendPlanDO attendPlanDO) {

        MobilePunchVO mobilePunchVO = new MobilePunchVO();

        AttendPlanDO usedAttendPlanDO = null;
        String locationStr = "";

        List<AttendRuleLocationAddDTO> locationList = JSON.parseArray(attendPlanDO.getPunchLocationJson(), AttendRuleLocationAddDTO.class);
        if (locationList == null) {
            throw new HrException("未设置打卡地点");
        }
        //打卡地点
        LngLat end = new LngLat(Double.parseDouble(longitude), Double.parseDouble(latitude));
        // 如果打卡的位置都不在所有的打卡范围内,找出一个距离最近的返回前端显示
        Double minDifference = null;
        boolean isRange = false;
        for (AttendRuleLocationAddDTO location : locationList) {
            // 设置的打卡范围
            Integer thisDistance = location.getDistance();
            //公司地点
            LngLat start = new LngLat(Double.parseDouble(location.getPunchLongitude()), Double.parseDouble(location.getPunchLatitude()));
            //获取公司地点和打卡地点相差的距离
            double distance = AMapUtils.calculateLineDistance(start, end);
            if (distance <= thisDistance) {
                usedAttendPlanDO = attendPlanDO;
                locationStr = location.getPositionName();
                isRange = true;
                break;
            } else {
                // 算一算超过了多少米
                double exceed = distance - thisDistance;
                if (ObjectUtils.isEmpty(minDifference)) {
                    minDifference = exceed;
                } else {
                    minDifference = Math.min(exceed, minDifference);
                }
            }
        }

        if (!isRange) {
            mobilePunchVO.setMinDifference(minDifference);
        }
        mobilePunchVO.setAttendPlanDO(usedAttendPlanDO);
        mobilePunchVO.setLocation(locationStr);

        return mobilePunchVO;
    }

    /**
     * WIFI打卡
     *
     * @param attendPlanDOList
     * @param wifiName
     * @param macAddr
     * @return
     */
    private AttendPlanDO wifiPunch(List<AttendPlanDO> attendPlanDOList, String wifiName, String macAddr) {

        AttendPlanDO attendPlanDO = null;

        for (AttendPlanDO attendPlan : attendPlanDOList) {

            String punchWifiJson = attendPlan.getPunchWifiJson();
            if (StringUtils.isNotBlank(punchWifiJson)) {
                List<AttendRuleWifiAddDTO> wifiList = JSON.parseArray(punchWifiJson, AttendRuleWifiAddDTO.class);
                if (wifiList != null) {
                    for (AttendRuleWifiAddDTO wifi : wifiList) {
                        if (wifi.getName().equals(wifiName)
                                && wifi.getMacAddr().equals(macAddr)) {
                            attendPlanDO = attendPlan;
                            break;
                        }
                    }
                }
            }
        }

        return attendPlanDO;
    }

    /**
     * 考勤打卡
     *
     * @param attendPunchDTO 考勤打卡
     * @param tokenUser
     * @return
     */
    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public AttendPunchVO punch(AttendPunchDTO attendPunchDTO, TokenUser tokenUser) {

        AttendPunchVO attendPunchVO = new AttendPunchVO();

        String punchCheckJson = cache.get(CacheKeyKit.getAttendPunchCheckInfo(attendPunchDTO.getPunchNumber()));
        if (punchCheckJson == null) {
            throw new HrException("请重新检查打卡范围");
        }

        AttendPunchCheckVO punchCheck = JSON.parseObject(punchCheckJson, AttendPunchCheckVO.class);

        if (punchCheck.getRangeState() == 0) {

            // 范围外打卡 ; 1-允许范围外打卡，记录为地点异常,2-允许范围外打卡，记录为正常外勤,3-不允许范围外打卡
            if (punchCheck.getPunchRange() == 3) {
                throw new HrException("不允许范围外打卡");
            }
//			else if (punchCheck.getPunchRange() == 1) {
//				punchCheck.setPunchStatus(6);
//			}
            // 外出打卡
//			else if (punchCheck.getPunchRange() == 2) {
//				AttendGoOutDTO attendGoOutDTO = new AttendGoOutDTO();
//				attendGoOutDTO.setLongitude(attendPunchDTO.getLongitude());
//				attendGoOutDTO.setLatitude(attendPunchDTO.getLatitude());
//				attendGoOutDTO.setLocation(punchCheck.getPunchLocation());
//				attendGoOutDTO.setOutExplain("范围外打卡-记录为正常外勤");
//				attendGoOutDTO.setRemark("");
//
//				attendGoOutService.punch(attendGoOutDTO, tokenUser);
//			}
        }
//    	if (punchCheck.getWorkStatus() == 3) {
//			throw new HrException("今日已完成打卡");
//		}
//    	if (punchCheck.getIsWhitelist() || !punchCheck.getIsNeedPunchOfDaily()
//										|| !punchCheck.getIsTodayAttend()) {
//			throw new HrException("今日不需要打卡");
//		}
        // 当前时间
        Date now = DateKit.getToday();

        // 打卡时间
        String punchTime = DateFormatUtils.format(now, "HH:mm:ss");

        // 查询员工信息
        EmpInfoDO empInfoDO = this.getEmpInfoDO(tokenUser);

        // 是否已设置考勤规则
        Boolean isSetPlan = true;

        // 查询当前用户使用的考勤规则
        AttendPlanDO attendPlanDO = attendPlanService.selectDOById(punchCheck.getAttendPlanId(), tokenUser.getSiteId());
        if (attendPlanDO == null) {
//			throw new HrException("未设置考勤规则");
            isSetPlan = false;
            attendPunchVO.setIsSetPlan(isSetPlan);
            return attendPunchVO;
        }

        // 年
        String yearly = DateFormatUtils.format(new Date(), "yyyy");
        // 月
        String monthly = DateFormatUtils.format(new Date(), "MM");
        // 日
        String daily = DateFormatUtils.format(new Date(), "dd");

        // 上班还是下班;1-上班打卡,2-下班打卡,3-今日已完成打卡
        int workStatus = punchCheck.getWorkStatus();

        String standardTime = "";
        Integer workTime = 0;

        // 如果是上班打卡
        if (workStatus == 1) {
            standardTime = punchCheck.getOnWorkTime();
        }

        // 如果是下班打卡
        if (workStatus == 2 || workStatus == 3) {
            standardTime = punchCheck.getOffWorkTime();
            // 计算今日工作时长
            workTime = punchCheck.getWorkTime();
            // 查询当前员工是第几次打的下班卡
            AttendPunchRecordQuery attendPunchRecordQuery = new AttendPunchRecordQuery();
            attendPunchRecordQuery.setYear(yearly);
            attendPunchRecordQuery.setMonth(monthly);
            attendPunchRecordQuery.setDay(daily);
            attendPunchRecordQuery.setCompanyId(punchCheck.getCompanyId());
            attendPunchRecordQuery.setEmpInfoId(empInfoDO.getId());
            attendPunchRecordQuery.setPunchType(1);
            List<Order> orderList = new ArrayList<>();
            // 升序查询上班打卡记录
            orderList.add(new Order("standardTime", false));
            attendPunchRecordQuery.setOrders(orderList);
            List<AttendPunchRecordDO> attendUp = attendPunchRecordService.selectDO(attendPunchRecordQuery, tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(attendUp)) {
                // 计算今日工作时长
                workTime = DateUtils.getMinutesDiff(now, attendUp.get(attendUp.size() - 1).getPunchTime());
                // 减去休息时间
                workTime -= calculateRestPeriodTime(attendPlanDO, tokenUser, attendUp.get(attendUp.size() - 1), workTime);
            }
            // 查询当前最后一条下班记录  把里面的工作时长设置为0
            attendPunchRecordQuery.setPunchType(2);
            List<Order> orderListTwo = new ArrayList<>();
            // 降序查询下班打卡记录
            orderListTwo.add(new Order("gmtCreate", true));
            attendPunchRecordQuery.setOrders(orderListTwo);
            List<AttendPunchRecordDO> attendOff = attendPunchRecordService.selectDO(attendPunchRecordQuery, tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(attendOff)) {
                if (!CollectionUtils.isEmpty(attendUp) && attendUp.size() > 1) {
                    // 两条上班卡记录  判断最后一条的下班卡是否为第二次的下班卡
                    if (attendOff.get(0).getStandardTime().getTime()
                            > attendUp.get(attendUp.size() - 1).getStandardTime().getTime()) {
                        // 最后一条的下班卡记录时间 大于 最后的一次上班卡的时间  则修改最后的一条下班记录的工作时长为0
                        AttendPunchRecordUpdateDTO attendPunchRecordUpdateDTO = new AttendPunchRecordUpdateDTO();
                        attendPunchRecordUpdateDTO.setId(attendOff.get(0).getId());
                        attendPunchRecordUpdateDTO.setWorkTime(0);
                        attendPunchRecordService.update(attendPunchRecordUpdateDTO, tokenUser);
                    }
                } else {
                    // 一条上班卡记录 修改最后的一条下班记录的工作时长为0
                    AttendPunchRecordUpdateDTO attendPunchRecordUpdateDTO = new AttendPunchRecordUpdateDTO();
                    attendPunchRecordUpdateDTO.setId(attendOff.get(0).getId());
                    attendPunchRecordUpdateDTO.setWorkTime(0);
                    attendPunchRecordService.update(attendPunchRecordUpdateDTO, tokenUser);
                }
            }
        }

        AttendPunchRecordAddDTO record = new AttendPunchRecordAddDTO();
        // 公司ID
        record.setCompanyId(punchCheck.getCompanyId());
        // 公司名称
        record.setCompanyName(punchCheck.getCompanyName());
        // 员工ID
        record.setEmpInfoId(empInfoDO.getId());
        // 员工姓名
        record.setEmpInfoName(empInfoDO.getName());
        // 考勤方案
        record.setAttendPlanId(attendPlanDO.getId());
        // 考勤方案名称
        record.setAttendPlanName(attendPlanDO.getName());
        // 年月日
        record.setYear(yearly);
        record.setMonth(monthly);
        record.setDay(daily);
        // 打卡类型（1：上班打卡，2：下班打卡，3：上班缺卡:4：下班缺卡）
        if (punchCheck.getWorkStatus() == 3) {
            // 今日已完成打卡
            punchCheck.setWorkStatus(2);
        }
        record.setPunchType(punchCheck.getWorkStatus());
        // 打卡范围（1：范围外打卡，地点异常，2：范围外打卡，正常外勤，3：不允许范围外打卡）
        record.setPunchRange(punchCheck.getPunchRange());
        // 标准时间
        record.setStandardTime(DateKit.getDate("yyyy-MM-dd HH:mm", standardTime));
        // 打卡时间
        record.setPunchTime(now);
        // 打卡状态
        record.setPunchStatus(punchCheck.getPunchStatus());
        // 迟到/早退分钟数
        record.setMinute(punchCheck.getMinutes());
        // 打卡坐标
        record.setCoordinate(attendPunchDTO.getLongitude() + "," + attendPunchDTO.getLatitude());
        // 打卡wifi
        record.setPunchWifi(punchCheck.getWifiName());
        // 打卡地点
        record.setLocation(punchCheck.getPunchLocation());
        // 打卡wifi
        record.setPunchWifi(attendPunchDTO.getWifiName() + "," + attendPunchDTO.getMacAddr());
        // 打卡来源 ; 1-app打卡,2-考勤机打卡
        record.setPunchSource(1);
        // 是否校准
        record.setIsCalibration(false);
        // 是否补卡 ; 1-是,0-否
        record.setIsMakeUp(false);
        record.setPunchSerial(punchCheck.getPunchSerial());

        record.setHisStandardTime(standardTime);
        record.setHisPunchStatus(punchCheck.getPunchStatus());
        record.setWorkTime(workTime);
        record.setRangeState(attendPunchDTO.getRangeState());
//		record.setHisPunchTime(punchTime);

        // 查询员工所在部门
        DepInfoDO depInfo = this.getDepInfo(empInfoDO.getId(), tokenUser.getSiteId());
        if (depInfo != null) {
            record.setDeptId(depInfo.getId());
            record.setDeptName(depInfo.getName());
        }

//		AttendPunchRecordVO attendPunchRecordVO = attendPunchRecordService.save(record, tokenUser);

        // 转DO
        AttendPunchRecordDO attendPunchRecordDO = record.to(AttendPunchRecordDO.class);
        attendPunchRecordDO.setValid(true);
        attendPunchRecordDO.setGmtCreate(now);
        attendPunchRecordDO.setGmtModified(now);
        attendPunchRecordDO.setCreateUserId(tokenUser.getUserId());
        attendPunchRecordDO.setModifiedUserId(tokenUser.getUserId());
        attendPunchRecordDO.setSiteId(tokenUser.getSiteId());
        attendPunchRecordDAO.insertSelective(attendPunchRecordDO);

//		if (workStatus != 3) {
//
//			// 考勤打卡日统计
//			this.summaryAttendPunchDaily(punchCheck, empInfoDO, punchTime, tokenUser);
//
//			//考勤人员月统计
//			attendPeopleMonthlyStatService.monthlyStat(attendPunchRecordVO, punchCheck.getPeriod(), tokenUser);
//
//			// 统计加班
//			if (StringUtils.isNotBlank(punchCheck.getEndOffWorkTime())) {
//
//				if (punchCheck.getEndOffWorkTime().equals(punchCheck.getOffWorkTime())) {
//
//					AutoCalculOverTimeDTO autoCalculOverTimeDTO = new AutoCalculOverTimeDTO();
//					autoCalculOverTimeDTO.setAttendPlanId(punchCheck.getAttendPlanId());
//					autoCalculOverTimeDTO.setEmpInfoId(empInfoDO.getId());
//					autoCalculOverTimeDTO.setOffWorkTime(punchCheck.getEndOffWorkTime());
//					autoCalculOverTimeDTO.setPunchTime(punchCheck.getOffWorkPunchTime());
//					autoCalculOverTimeDTO.setYearly(yearly);
//					autoCalculOverTimeDTO.setMonthly(monthly);
//					autoCalculOverTimeDTO.setDaily(daily);
//					// 日期类型 1-工作日,2-休息日,3-法定节假日
//					int dateType = 0;
//					// 今日是否需要考勤;true-是,false-否
//					if (punchCheck.getIsTodayAttend()) {
//						dateType = 1;
//					}
//					else {
//						dateType = 2;
//					}
//					// 特殊日期是否需要打卡;true-是,false-否
//					if (punchCheck.getIsNeedPunchOfDaily()) {
//						dateType = 1;
//					}
//					else {
//						dateType = 2;
//					}
//					autoCalculOverTimeDTO.setDateType(dateType);
//					attendOvertimeRuleService.autoCalculOverTime(autoCalculOverTimeDTO, tokenUser);
//				}
//			}
//		}

        attendPunchVO = punchCheck.to(AttendPunchVO.class);
        attendPunchVO.setCompletedWork(true);
        attendPunchVO.setIsSetPlan(isSetPlan);
        return attendPunchVO;
    }

    /**
     * 根据考勤规则时间的设置  计算本次工作时间内的休息时间
     *
     * @param attendPlanDO        考勤规则
     * @param tokenUser           当前用户
     * @param attendPunchRecordDO 上班时间
     * @param workTime            工作时间
     * @return 工作时间内包含的休息时间
     */
    private int calculateRestPeriodTime(AttendPlanDO attendPlanDO, TokenUser tokenUser,
                                        AttendPunchRecordDO attendPunchRecordDO, Integer workTime) {
        // 计算休息时间  先查询休息时间
        AttendExtraJsonDO attendExtraJsonDO = attendExtraJsonService.selectByAttendPlanIdAndExtraType(
                attendPlanDO.getId(), ExtraType.ATTEND_PUNCH_TIME.getValue(), tokenUser);
        ParamException.notNull(attendExtraJsonDO, "根据规则id没有找到对应的时间设置，请联系管理员或刷新重试");
        String jsonData = attendExtraJsonDO.getJsonData();
        List<AttendRulePunchTimeAddDTO> attendRulePunchTimeAddDTO = JSON.parseArray(jsonData, AttendRulePunchTimeAddDTO.class);
        int restPeriodTime = 0;
        List<AttendPeriodDTO> attendPeriodDTOList = attendRulePunchTimeAddDTO.get(0).getPeriodList();
        AttendPeriodDTO attendPeriodDTO = attendRulePunchTimeAddDTO.get(0).getRestPeriod();

        if (!CollectionUtils.isEmpty(attendPeriodDTOList)
                && attendPeriodDTOList.get(0).getHasRestPeriod()
                && Objects.nonNull(attendPeriodDTO)
                && StrKit.isNotEmpty(attendPeriodDTO.getStartPeriod())) {
            // 构造四个时间 上班时间 下班时间 开始休息时间 结束休息时间
            Date upWork = attendPunchRecordDO.getPunchTime();
            Date downWork = new Date();
            String strStartRestPeriod = DateKit8.format(new Date(), DateKit8.DATE) + " " + attendPeriodDTO.getStartPeriod();
            String strEndRestPeriod = DateKit8.format(new Date(), DateKit8.DATE) + " " + attendPeriodDTO.getEndPeriod();
            Date startRestPeriod = DateKit8.parse(strStartRestPeriod + ":00", DateKit8.DATE_TIME);
            Date endRestPeriod = DateKit8.parse(strEndRestPeriod + ":00", DateKit8.DATE_TIME);
            // 1 上下班时间端包含全部休息时间端点   即：需要减去全部休息时间
            if (upWork.getTime() <= startRestPeriod.getTime()
                    && downWork.getTime() >= endRestPeriod.getTime()) {
                restPeriodTime = DateUtils.getMinutesDiff(endRestPeriod, startRestPeriod);
            }
            // 2 上下班时间仅包含左端点 工作时间需要减去开始休息时间到下班时间的差值
            if (upWork.getTime() <= startRestPeriod.getTime()
                    && downWork.getTime() >= startRestPeriod.getTime()
                    && downWork.getTime() <= endRestPeriod.getTime()) {
                restPeriodTime = DateUtils.getMinutesDiff(downWork, startRestPeriod);
            }
            // 3 上下班时间仅包含右端点 工作时间需要减去上班时间到结束休息时间的差值
            if (upWork.getTime() >= startRestPeriod.getTime()
                    && upWork.getTime() <= endRestPeriod.getTime()
                    && downWork.getTime() >= endRestPeriod.getTime()) {
                restPeriodTime = DateUtils.getMinutesDiff(endRestPeriod, upWork);
            }
            // 上下班时间在休息时段内   工作时间为0
            if (upWork.getTime() >= startRestPeriod.getTime()
                    && downWork.getTime() <= endRestPeriod.getTime()) {
                restPeriodTime = workTime;
            }
        }
        return restPeriodTime;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public AttendPunchVO punch2(AttendPunchDTO attendPunchDTO, TokenUser tokenUser) {

        AttendPunchVO attendPunchVO = new AttendPunchVO();

        String punchCheckJson = cache.get(CacheKeyKit.getAttendPunchCheckInfo(attendPunchDTO.getPunchNumber()));
        if (punchCheckJson == null) {
            throw new HrException("请重新检查打卡范围");
        }

        AttendPunchCheckVO punchCheck = JSON.parseObject(punchCheckJson, AttendPunchCheckVO.class);

        if (punchCheck.getRangeState() == 0) {

            // 范围外打卡 ; 1-允许范围外打卡，记录为地点异常,2-允许范围外打卡，记录为正常外勤,3-不允许范围外打卡
            if (punchCheck.getPunchRange() == 3) {
                throw new HrException("不允许范围外打卡");
            } else if (punchCheck.getPunchRange() == 1) {
                punchCheck.setPunchStatus(6);
            }
            // 外出打卡
            else if (punchCheck.getPunchRange() == 2) {
                AttendGoOutDTO attendGoOutDTO = new AttendGoOutDTO();
                attendGoOutDTO.setLongitude(attendPunchDTO.getLongitude());
                attendGoOutDTO.setLatitude(attendPunchDTO.getLatitude());
                attendGoOutDTO.setLocation(punchCheck.getPunchLocation());
                attendGoOutDTO.setOutExplain("范围外打卡-记录为正常外勤");
                attendGoOutDTO.setRemark("");

                attendGoOutService.punch(attendGoOutDTO, tokenUser);
            }
        }
        if (punchCheck.getWorkStatus() == 3) {
            throw new HrException("今日已完成打卡");
        }
//    	if (punchCheck.getIsWhitelist() || !punchCheck.getIsNeedPunchOfDaily()
//										|| !punchCheck.getIsTodayAttend()) {
//			throw new HrException("今日不需要打卡");
//		}

        // 打卡时间
        String punchTime = DateFormatUtils.format(new Date(), "HH:mm:ss");

        // 随机获取时间
        punchTime = new SimpleDateFormat("HH:mm:ss").format(randomDate("2019-01-01", "2019-01-31"));

        // 查询员工信息
        EmpInfoDO empInfoDO = this.getEmpInfoDO(tokenUser);
        // 查询部门信息
        DepInfoDO depInfo = this.getDepInfo(empInfoDO.getId(), tokenUser.getSiteId());

        // 查询当前用户使用的考勤规则
        AttendPlanDO attendPlanDO = attendPlanService.selectDOById(punchCheck.getAttendPlanId(), tokenUser.getSiteId());
        if (attendPlanDO == null) {
            throw new HrException("未设置考勤规则");
        }

        // 年
        String yearly = DateFormatUtils.format(new Date(), "yyyy");
        // 月
        String monthly = DateFormatUtils.format(new Date(), "MM");
        // 日
        String daily = DateFormatUtils.format(new Date(), "dd");

        // 上班还是下班;1-上班打卡,2-下班打卡,3-今日已完成打卡
        int workStatus = punchCheck.getWorkStatus();

        String standardTime = "";

        // 如果是上班打卡
        if (workStatus == 1) {
            standardTime = punchCheck.getOnWorkTime();
        }

        // 如果是下班打卡
        if (workStatus == 2) {
            standardTime = punchCheck.getOffWorkTime();
        }

        AttendPunchRecordAddDTO record = new AttendPunchRecordAddDTO();
        // 公司ID
        record.setCompanyId(punchCheck.getCompanyId());
        // 公司名称
        record.setCompanyName(punchCheck.getCompanyName());
        // 部门ID
        record.setDeptId(depInfo != null ? depInfo.getId() : "");
        // 部门名称
        record.setDeptName(depInfo != null ? depInfo.getName() : "");
        // 员工ID
        record.setEmpInfoId(empInfoDO.getId());
        // 员工姓名
        record.setEmpInfoName(empInfoDO.getName());
        // 考勤方案
        record.setAttendPlanId(attendPlanDO.getId());
        // 考勤方案名称
        record.setAttendPlanName(attendPlanDO.getName());
        // 年月日
        record.setYear(yearly);
        record.setMonth(monthly);
        record.setDay(daily);
        // 打卡类型 ; 0-无效打卡,1-上班打卡,2-下班打卡,3-完成打卡
        record.setPunchType(punchCheck.getWorkStatus());
        // 标准时间
//		record.setStandardTime(standardTime);
        // 打卡时间
//		record.setPunchTime(punchTime);
        // 打卡状态
        record.setPunchStatus(punchCheck.getPunchStatus());
        // 迟到/早退分钟数
        record.setMinute(punchCheck.getMinutes());
        // 打卡坐标
        record.setCoordinate(attendPunchDTO.getLongitude() + "," + attendPunchDTO.getLatitude());
        // 打卡地点
        record.setLocation(punchCheck.getPunchLocation());
        // 打卡wifi
        record.setPunchWifi(attendPunchDTO.getWifiName() + "," + attendPunchDTO.getMacAddr());
        // 打卡来源 ; 1-app打卡,2-考勤机打卡
        record.setPunchSource(1);
        // 是否补卡 ; 1-是,0-否
        record.setIsMakeUp(false);
        record.setPunchSerial(punchCheck.getPunchSerial());

        AttendPunchRecordVO attendPunchRecordVO = attendPunchRecordService.save(record, tokenUser);

        if (workStatus != 3) {

            // 考勤打卡日统计
            this.summaryAttendPunchDaily(punchCheck, empInfoDO, punchTime, tokenUser);

            //考勤人员月统计
            attendPeopleMonthlyStatService.monthlyStat(attendPunchRecordVO, punchCheck.getPeriod(), tokenUser);

            // 统计加班
            if (StringUtils.isNotBlank(punchCheck.getEndOffWorkTime())) {

                if (punchCheck.getEndOffWorkTime().equals(punchCheck.getOffWorkTime())) {

                    AutoCalculOverTimeDTO autoCalculOverTimeDTO = new AutoCalculOverTimeDTO();
                    autoCalculOverTimeDTO.setAttendPlanId(punchCheck.getAttendPlanId());
                    autoCalculOverTimeDTO.setEmpInfoId(empInfoDO.getId());
                    autoCalculOverTimeDTO.setOffWorkTime(punchCheck.getEndOffWorkTime());
                    autoCalculOverTimeDTO.setPunchTime(punchCheck.getOffWorkPunchTime());
                    autoCalculOverTimeDTO.setYearly(yearly);
                    autoCalculOverTimeDTO.setMonthly(monthly);
                    autoCalculOverTimeDTO.setDaily(daily);
                    // 日期类型 1-工作日,2-休息日,3-法定节假日
                    int dateType = 0;
                    // 今日是否需要考勤;true-是,false-否
                    if (punchCheck.getIsTodayAttend()) {
                        dateType = 1;
                    } else {
                        dateType = 2;
                    }
                    // 特殊日期是否需要打卡;true-是,false-否
                    if (punchCheck.getIsNeedPunchOfDaily()) {
                        dateType = 1;
                    } else {
                        dateType = 2;
                    }
                    autoCalculOverTimeDTO.setDateType(dateType);
                    attendOvertimeRuleService.autoCalculOverTime(autoCalculOverTimeDTO, tokenUser);
                }
            }
        }

        attendPunchVO = punchCheck.to(AttendPunchVO.class);
        attendPunchVO.setPunchRecordId(attendPunchRecordVO.getId());

        return attendPunchVO;
    }

    private static Date randomDate(String beginDate, String endDate) {
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            Date start = format.parse(beginDate);
            Date end = format.parse(endDate);

            if (start.getTime() >= end.getTime()) {
                return null;
            }
            long date = random(start.getTime(), end.getTime());
            return new Date(date);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static long random(long begin, long end) {
        long rtn = begin + (long) (Math.random() * (end - begin));
        if (rtn == begin || rtn == end) {
            return random(begin, end);
        }
        return rtn;
    }

    /**
     * 考勤打卡日统计
     *
     * @param punchCheck
     * @param empInfoDO
     * @param tokenUser
     */
    private void summaryAttendPunchDaily(AttendPunchCheckVO punchCheck, EmpInfoDO empInfoDO, String punchTime, TokenUser tokenUser) {

        // 年
        String yearly = DateFormatUtils.format(new Date(), "yyyy");
        // 月
        String monthly = DateFormatUtils.format(new Date(), "MM");
        // 日
        String daily = DateFormatUtils.format(new Date(), "dd");

        // 考勤报表
        // 考勤打卡日汇总 h_attend_punch_daily_summary
        AttendPunchDailySummaryQuery summaryQuery = new AttendPunchDailySummaryQuery();
        summaryQuery.setCompanyId(punchCheck.getCompanyId());
        summaryQuery.setYearly(yearly);
        summaryQuery.setMonthly(monthly);
        summaryQuery.setDaily(daily);
        List<AttendPunchDailySummaryDO> summaryDataList = attendPunchDailySummaryService.selectDO(summaryQuery, tokenUser.getSiteId());

        if (summaryDataList.size() > 0) {

            AttendPunchDailySummaryDO summaryDO = summaryDataList.get(0);

            AttendPunchDailySummaryDO summaryDOEntity = new AttendPunchDailySummaryDO();
            summaryDOEntity.setId(summaryDO.getId());

            // 如果是下班打卡
            if (punchCheck.getWorkStatus() == 2) {
                // 早退
                if (punchCheck.getPunchStatus() == 3) {
                    // 早退人次
                    summaryDOEntity.setEarlyPeoples(summaryDO.getEarlyPeoples() + 1);
                }

                // 查询当前打卡人员今日有没有打卡
                AttendPunchRecordQuery attendPunchRecordQuery = new AttendPunchRecordQuery();
                attendPunchRecordQuery.setEmpInfoId(empInfoDO.getId());
                attendPunchRecordQuery.setYear(yearly);
                attendPunchRecordQuery.setMonth(monthly);
                attendPunchRecordQuery.setDay(daily);
                attendPunchRecordQuery.setPunchType(2);
                List<AttendPunchRecordDO> todayRecordList = attendPunchRecordService.selectDO(attendPunchRecordQuery, tokenUser.getSiteId());
                if (todayRecordList.size() <= 0) {
                    // 实际出勤人数
                    summaryDOEntity.setActualPeoples(summaryDO.getActualPeoples() + 1);
                }

                // 下班缺卡人数
                summaryDOEntity.setOffWorkMissPeoples(0);
            }

            // 如果是上班打卡，打卡状态是迟到
            if (punchCheck.getWorkStatus() == 1) {
                // 上班迟到
                if (punchCheck.getPunchStatus() == 2) {
                    // 迟到人次
                    summaryDOEntity.setLatePeoples(summaryDO.getLatePeoples() + 1);
                }
                // 上班缺卡人数
                summaryDOEntity.setOnWorkMissPeoples(0);
            }
            summaryDOEntity.setIsUsed(1);
            // 请假人数
            // dailySummary.setLeavePeoples(leavePeoples);
            attendPunchDailySummaryService.updateByIdSelective(summaryDOEntity, tokenUser.getSiteId(), tokenUser.getUserId());
        }

        // 考勤打卡日报表 h_attend_punch_daily_Report
        AttendPunchDailyReportQuery reportQuery = new AttendPunchDailyReportQuery();
        reportQuery.setCompanyId(punchCheck.getCompanyId());
        reportQuery.setEmpInfoId(empInfoDO.getId());
        reportQuery.setYearly(yearly);
        reportQuery.setMonthly(monthly);
        reportQuery.setDaily(daily);

        List<AttendPunchDailyReportDO> reportDataList = attendPunchDailyReportService.selectDO(reportQuery, tokenUser.getSiteId());
        if (reportDataList.size() > 0) {
            AttendPunchDailyReportDO reportDO = reportDataList.get(0);

            AttendPunchDailyReportDO reportDOEntity = new AttendPunchDailyReportDO();

            reportDOEntity.setId(reportDO.getId());
            reportDOEntity.setEarliestTime(reportDO.getEarliestTime());
            reportDOEntity.setLatestTime(reportDO.getLatestTime());

            // 如果是上班打卡
            if (punchCheck.getWorkStatus() == 1) {

                // 签到地点
                reportDOEntity.setSignInLocation(punchCheck.getPunchLocation());
                // 最早上班打卡时间
                if (reportDO.getEarliestTime() == null || StringUtils.isBlank(reportDO.getEarliestTime())) {
                    reportDOEntity.setEarliestTime(punchTime);
                }

            }

            // 如果是下班打卡
            if (punchCheck.getWorkStatus() == 2) {
                // 签退地点
                reportDOEntity.setSignOutLocation(punchCheck.getPunchLocation());
                // 最晚打卡时间
                reportDOEntity.setLatestTime(punchTime);
            }
            // 设置为异常 历史考勤状态
            if (punchCheck.getPunchStatus() != 1) {
                reportDOEntity.setHisAttendStatus(2);
            }
            // 历史考勤状态
            else {
                reportDOEntity.setHisAttendStatus(1);
            }
            reportDOEntity.setIsUsed(1);

            // 打卡次数
            reportDOEntity.setPunchCounts(reportDO.getPunchCounts() + 1);
            // 工作时长(分钟)
            if (reportDOEntity.getEarliestTime() != null && StringUtils.isNotBlank(reportDOEntity.getEarliestTime())
                    && reportDOEntity.getLatestTime() != null && StringUtils.isNotBlank(reportDOEntity.getLatestTime())) {

                int startL = Integer.valueOf(reportDOEntity.getEarliestTime().split(":")[0]).intValue() * 60;
                int startR = Integer.valueOf(reportDOEntity.getEarliestTime().split(":")[1]).intValue();
                int startMin = startL + startR;

                int endL = Integer.valueOf(reportDOEntity.getLatestTime().split(":")[0]).intValue() * 60;
                int endR = Integer.valueOf(reportDOEntity.getLatestTime().split(":")[1]).intValue();
                int endMin = endL + endR;

                if (startMin < endMin) {
                    reportDOEntity.setWorkTimeMinute(endMin - startMin);
                } else if (startMin > endMin) {
                    reportDOEntity.setWorkTimeMinute(24 * 60 - startMin + endMin);
                }
            }

            // 加班状态;1-加班,0-未加班
            // 迟到或早退分钟数
            reportDOEntity.setMinutes(punchCheck.getMinutes());

            // 更新
            attendPunchDailyReportService.updateByIdSelective(reportDOEntity, tokenUser.getSiteId(), tokenUser.getUserId());
        }
    }

    /**
     * 考勤人员月统计
     *
     * @param punchCheck
     * @param empInfoDO
     * @param tokenUser
     */
    private void statAttendPeopleMonthly(AttendPunchCheckVO punchCheck, EmpInfoDO empInfoDO, TokenUser tokenUser) {

        // 年
        String yearly = DateFormatUtils.format(new Date(), "yyyy");
        // 月
        String monthly = DateFormatUtils.format(new Date(), "MM");
        AttendPeopleMonthlyStatQuery attendPeopleMonthlyStatQuery = new AttendPeopleMonthlyStatQuery();

        attendPeopleMonthlyStatQuery.setEmpInfoId(empInfoDO.getId());
        attendPeopleMonthlyStatQuery.setYearly(yearly);
        attendPeopleMonthlyStatQuery.setMonthly(monthly);

        List<AttendPeopleMonthlyStatDO> monthlyStatDODataList = attendPeopleMonthlyStatService.selectDO(attendPeopleMonthlyStatQuery, tokenUser.getSiteId());
        if (monthlyStatDODataList.size() > 0) {

            AttendPeopleMonthlyStatDO attendPeopleMonthlyStatDO = monthlyStatDODataList.get(0);

            AttendPeopleMonthlyStatDO monthlyStatDOEntity = new AttendPeopleMonthlyStatDO();

            monthlyStatDOEntity.setId(attendPeopleMonthlyStatDO.getId());

            // 如果是下班打卡
            if (punchCheck.getWorkStatus() == 2) {
                // 出勤天数
                monthlyStatDOEntity.setAttendDays(attendPeopleMonthlyStatDO.getAttendDays() + 1);
                // 下班缺卡次数
                monthlyStatDOEntity.setOffWorkMissCounts(attendPeopleMonthlyStatDO.getOffWorkMissCounts() - 1);
                // 旷工天数
                // monthlyStatDOEntity.setAbsenteeismDays(attendPeopleMonthlyStatDO.getAbsenteeismDays() - 1);
            }

            // 休息天数
            // monthlyStatDOEntity.setRestDays(restDays);

            // 如果是上班打卡
            if (punchCheck.getWorkStatus() == 1) {
                // 打卡状态是迟到
                if (punchCheck.getPunchStatus() == 2) {
                    // 迟到次数
                    monthlyStatDOEntity.setLateCounts(attendPeopleMonthlyStatDO.getLateCounts() + 1);
                }

                // 上班缺卡次数
                monthlyStatDOEntity.setOnWorkMissCounts(attendPeopleMonthlyStatDO.getOnWorkMissCounts() - 1);
            }

            // 更新
            attendPeopleMonthlyStatService.updateByIdSelective(monthlyStatDOEntity, tokenUser.getSiteId(), tokenUser.getUserId());
        }
    }

    /**
     * 查询员工信息
     *
     * @param tokenUser
     * @return
     */
    private EmpInfoDO getEmpInfoDO(TokenUser tokenUser) {

        // 查询员工信息
        EmpInfoQuery empInfoQuery = new EmpInfoQuery();
        empInfoQuery.setUserId(tokenUser.getUserId());
        EmpInfoDO empInfoDO = null;
        List<EmpInfoDO> empInfoDODataList = empInfoService.selectDO(empInfoQuery, tokenUser);
        if (empInfoDODataList.size() > 0) {
            // 过滤离职人员
            empInfoDODataList = empInfoDODataList.stream()
                    .filter(empInfo-> 4 != empInfo.getStatus()).collect(Collectors.toList());
            empInfoDO = empInfoDODataList.get(0);
        }

        log.info("查询员工信息:" + JSON.toJSONString(empInfoDO));

        return empInfoDO;
    }

    /**
     * 模拟批量考勤打卡
     *
     * @param attendPunchDTO
     * @return
     */
    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void punchBatchSimulation(AttendPunchDTO attendPunchDTO) throws InvocationTargetException, IllegalAccessException {
        String siteId = "73536A04368B4BB1B395775FE01E345E";
        String companyId = "932B5BB60904407FB8626B94B656AB00";
        TokenUser tokenUser = UserContext.getUser();
        tokenUser.setSiteId(siteId);

        EmpInfoQuery empInfoQuery = new EmpInfoQuery();
        empInfoQuery.setCompanyId(companyId);
        //查询一个公司下员工列表
        List<EmpInfoListVO> empInfoListVOList = empInfoService.select(empInfoQuery, tokenUser);
        //员工注册为用户并添加到考勤规则里
        for (EmpInfoListVO empInfoListVO : empInfoListVOList) {
            UserVO userVO = userProviderService.registerAccount(empInfoListVO.getName(), empInfoListVO.getPhone(), tokenUser, empInfoListVO.getCompanyId());
            tokenUser.setUserId(userVO.getId());

            AttendPunchStaffAddDTO attendPunchStaffAddDTO = new AttendPunchStaffAddDTO();
            String attendPlanId = "1837259ae1064a2c9f2d0b92880dd593";
            attendPunchStaffAddDTO.setCompanyId(companyId);
            attendPunchStaffAddDTO.setCompanyName("测试打卡公司、勿动");
            attendPunchStaffAddDTO.setAttendPlanId(attendPlanId);
            attendPunchStaffAddDTO.setEmpInfoId(empInfoListVO.getId());
            attendPunchStaffAddDTO.setEmpInfoName(empInfoListVO.getName());
            attendPunchStaffService.save(attendPunchStaffAddDTO, tokenUser);

            // todo 新增考勤方案权限校验
            AttendPunchCheckQuery attendPunchCheckQuery = new AttendPunchCheckQuery();
            attendPunchCheckQuery.setLatitude("40.010055");
            attendPunchCheckQuery.setLongitude("116.644009");
            attendPunchCheckQuery.setMacAddr("74:05:a5:d4:43:99");
            attendPunchCheckQuery.setPunchLocation("安泰广场1204测试");
            attendPunchCheckQuery.setWifiName("京谱123-5g");

            for (int i = 1; i <= 31; i++) {
                // 年
                String yearly = "2020";
                // 月
                String monthly = "01";
                // 日
                String daily = i < 10 ? "0" + i : "" + i;

                AttendPunchCheckVO attendPunchCheckVO = this.checkPunchBatchTest(attendPunchCheckQuery, tokenUser, yearly, monthly, daily);

                AttendPunchCheckSimpleVO attendPunchCheckSimpleVO = attendPunchCheckVO.to(AttendPunchCheckSimpleVO.class);

                if (attendPunchCheckSimpleVO.getRangeState() == 0) {
                    System.out.println("不在打卡范围内");
                    continue;
                } else if (!attendPunchCheckSimpleVO.getIsTodayAttend()) {
                    System.out.println("今日不需要考勤");
                    continue;
                } else if (attendPunchCheckSimpleVO.getIsWhitelist()) {
                    System.out.println("员工在白名单中不需要打卡");
                    continue;
                } else if (attendPunchCheckSimpleVO.getIsSpecialDate() && !attendPunchCheckSimpleVO.getIsNeedPunchOfDaily()) {
                    System.out.println("特殊日期且不需要打卡");
                    continue;
                } else if (attendPunchCheckSimpleVO.getCompletedWork()) {
                    System.out.println("上班打卡或下班打卡已完成");
                    continue;
                }

                BeanUtils.copyProperties(attendPunchCheckQuery, attendPunchDTO);
                attendPunchDTO.setPunchNumber(attendPunchCheckVO.getPunchNumber());
                AttendPunchVO attendPunchVO = this.punchBatchTest(attendPunchDTO, tokenUser, yearly, monthly, daily);
                System.out.println("年：" + yearly + "月：" + monthly + "日：" + daily + "：打卡成功=========================================");
            }

        }
    }

    /**
     * 模拟批量检查考勤打卡
     *
     * @param attendPunchCheckQuery 检查考勤打卡参数
     * @param tokenUser             当前用户
     * @return
     */
    @Override
    public AttendPunchCheckVO checkPunchBatchTest(AttendPunchCheckQuery attendPunchCheckQuery, TokenUser tokenUser, String yearly, String monthly, String daily) {

        // 当前的考勤日期
        String attendDateStr = yearly + "-" + monthly + "-" + daily;
        // 打卡时间
        String punchTimeStr = DateFormatUtils.format(new Date(), "HH:mm");
        // 查询员工信息
        EmpInfoDO empInfoDO = this.getEmpInfoDO(tokenUser);
        // 查询当前用户使用的考勤规则
        List<AttendPlanDO> attendPlanDOList = attendPlanService.getAttendPlan(empInfoDO.getId(), attendDateStr, tokenUser.getSiteId());
        if (attendPlanDOList == null) {
            throw new HrException("未设置考勤规则");
        }
        // 使用的考勤规则
        AttendPlanDO usedAttendPlan = null;

        // 是否在打卡范围内;1-是,0-否
        int rangeState = 0;
        // 如果是WIFI打卡, 是否在打卡范围内
        if (StringUtils.isNotBlank(attendPunchCheckQuery.getMacAddr())
                && StringUtils.isNotBlank(attendPunchCheckQuery.getWifiName())) {

            // WIFI打卡
            usedAttendPlan = this.wifiPunch(attendPlanDOList, attendPunchCheckQuery.getWifiName(), attendPunchCheckQuery.getMacAddr());
            if (usedAttendPlan != null) {
                rangeState = 1;
            }
        }

        // 如果WIFI不在打卡范围内，则判断移动网络
        if (rangeState == 0) {
            // 后台设置了打卡地点
            for (AttendPlanDO attendPlanDO : attendPlanDOList) {
                if (StringUtils.isNotBlank(attendPlanDO.getPunchLocationJson())) {
                    MobilePunchVO mobilePunchVO = this.mobilePunch(attendPunchCheckQuery.getLongitude(), attendPunchCheckQuery.getLatitude(), attendPlanDO);
                    if (mobilePunchVO.getAttendPlanDO() != null) {
                        rangeState = 1;
                        break;
                    }
                }
            }
        }

        if (rangeState == 0) {
            AttendPunchCheckVO attendPunchCheckVO = new AttendPunchCheckVO();
            attendPunchCheckVO.setRangeState(rangeState);
            return attendPunchCheckVO;
        }

        // 如果是明日生效  生效时间 ; 1-明日生效，2-立即生效
        if (usedAttendPlan.getTakeEffect() == 1) {
            // 并且当前的时间小于生效开始日期
            if (System.currentTimeMillis() < usedAttendPlan.getStartEffectTime().getTime()) {
                // 根据临时考勤规则ID查询
//				AttendPlanDO attendPlanDO = attendPlanService.selectDOById(usedAttendPlan.getTmpAttendPlanId(), usedAttendPlan.getSiteId());
//				usedAttendPlan = attendPlanDO;
            }
        }

        // 公共考勤打卡
        PunchCommonDTO punchCommonDTO = new PunchCommonDTO();

        punchCommonDTO.setAttendPlanId(usedAttendPlan.getId());
        punchCommonDTO.setPlanType(usedAttendPlan.getPlanType());
        punchCommonDTO.setEmpInfoId(empInfoDO.getId());
        punchCommonDTO.setAttendDateStr(attendDateStr);
        punchCommonDTO.setPunchTimeStr(punchTimeStr);
        punchCommonDTO.setIsSynchLegalHoliday(usedAttendPlan.getIsSynchLegalHoliday());

        PunchCommonVO punchCommonVO = attendPunchCommonService.punchCommon(punchCommonDTO, tokenUser);
        // 异常打卡
//		if (punchCommonVO.getWorkStatus() != 0) {
//			throw new HrException("未到打卡时间");
//		}

        // 如果有考勤考级设置
        AttendAdvancedDTO advanced = punchCommonVO.getAdvanced();

        String punchNumber = yearly + monthly + daily + DateFormatUtils.format(new Date(), "HHmmss") + HrKit.getRandom();
        AttendPunchCheckVO attendPunchCheckVO = punchCommonVO.to(AttendPunchCheckVO.class);
        // 打卡编号
        attendPunchCheckVO.setPunchNumber(punchNumber);
        // 是否在打卡范围内;1-是,0-否
        attendPunchCheckVO.setRangeState(rangeState);
        // 公司ID
        attendPunchCheckVO.setCompanyId(usedAttendPlan.getCompanyId());
        // 公司名称
        attendPunchCheckVO.setCompanyName(usedAttendPlan.getCompanyName());
        // 打卡地点
        attendPunchCheckVO.setPunchLocation("");
        // 考勤规则ID
        attendPunchCheckVO.setAttendPlanId(usedAttendPlan.getId());
        // 范围外打卡
        attendPunchCheckVO.setPunchRange(usedAttendPlan.getPunchRange());
        // 员工打卡时必须拍照
        attendPunchCheckVO.setIsTakePhoto(usedAttendPlan.getIsTakePhoto());
        // 只能拍照
        attendPunchCheckVO.setIsOnlyPhoto(usedAttendPlan.getIsOnlyPhoto());
        // 打卡地点
        attendPunchCheckVO.setPunchLocation(attendPunchCheckQuery.getPunchLocation());

        // 将打卡检查信息放入缓存
        cache.set(CacheKeyKit.getAttendPunchCheckInfo(punchNumber), JSON.toJSONString(attendPunchCheckVO), (20 * 60 * 1000));

        return attendPunchCheckVO;
    }

    /**
     * 模拟批量考勤打卡
     *
     * @param attendPunchDTO 考勤打卡参数
     * @param tokenUser
     * @return
     */
    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public AttendPunchVO punchBatchTest(AttendPunchDTO attendPunchDTO, TokenUser tokenUser, String yearly, String monthly, String daily) {

        AttendPunchVO attendPunchVO = new AttendPunchVO();

        String punchCheckJson = cache.get(CacheKeyKit.getAttendPunchCheckInfo(attendPunchDTO.getPunchNumber()));
        if (punchCheckJson == null) {
            throw new HrException("请重新检查打卡范围");
        }

        AttendPunchCheckVO punchCheck = JSON.parseObject(punchCheckJson, AttendPunchCheckVO.class);

        if (punchCheck.getRangeState() == 0) {

            // 范围外打卡 ; 1-允许范围外打卡，记录为地点异常,2-允许范围外打卡，记录为正常外勤,3-不允许范围外打卡
            if (punchCheck.getPunchRange() == 3) {
                throw new HrException("不允许范围外打卡");
            } else if (punchCheck.getPunchRange() == 1) {
                punchCheck.setPunchStatus(0);
            }
            // 外出打卡
            else if (punchCheck.getPunchRange() == 2) {
                AttendGoOutDTO attendGoOutDTO = new AttendGoOutDTO();
                attendGoOutDTO.setLongitude(attendPunchDTO.getLongitude());
                attendGoOutDTO.setLatitude(attendPunchDTO.getLatitude());
                attendGoOutDTO.setLocation(punchCheck.getPunchLocation());
                attendGoOutDTO.setOutExplain("范围外打卡-记录为正常外勤");
                attendGoOutDTO.setRemark("");

                attendGoOutService.punch(attendGoOutDTO, tokenUser);
            }
        }
        if (punchCheck.getWorkStatus() == 3) {
            throw new HrException("今日已完成打卡");
        }
//    	if (punchCheck.getIsWhitelist() || !punchCheck.getIsNeedPunchOfDaily()
//										|| !punchCheck.getIsTodayAttend()) {
//			throw new HrException("今日不需要打卡");
//		}

        // 打卡时间
        String punchTime = DateFormatUtils.format(new Date(), "HH:mm:ss");

        // 查询员工信息
        EmpInfoDO empInfoDO = this.getEmpInfoDO(tokenUser);

        // 查询当前用户使用的考勤规则
        AttendPlanDO attendPlanDO = attendPlanService.selectDOById(punchCheck.getAttendPlanId(), tokenUser.getSiteId());
        if (attendPlanDO == null) {
            throw new HrException("未设置考勤规则");
        }

//		// 年
//		String yearly = DateFormatUtils.format(new Date(), "yyyy");
//		// 月
//		String monthly = DateFormatUtils.format(new Date(), "MM");
//		// 日
//		String daily = DateFormatUtils.format(new Date(), "dd");

        // 上班还是下班;1-上班打卡,2-下班打卡,3-今日已完成打卡
        int workStatus = punchCheck.getWorkStatus();

        String standardTime = "";

        // 如果是上班打卡
        if (workStatus == 1) {
            standardTime = punchCheck.getOnWorkTime();
        }

        // 如果是下班打卡
        if (workStatus == 2) {
            standardTime = punchCheck.getOffWorkTime();
        }

        AttendPunchRecordAddDTO record = new AttendPunchRecordAddDTO();
        // 公司ID
        record.setCompanyId(punchCheck.getCompanyId());
        // 公司名称
        record.setCompanyName(punchCheck.getCompanyName());
        // 部门ID
        record.setDeptId("");
        // 部门名称
        record.setDeptName("");
        // 员工ID
        record.setEmpInfoId(empInfoDO.getId());
        // 员工姓名
        record.setEmpInfoName(empInfoDO.getName());
        // 考勤方案
        record.setAttendPlanId(attendPlanDO.getId());
        // 考勤方案名称
        record.setAttendPlanName(attendPlanDO.getName());
        // 年月日
        record.setYear(yearly);
        record.setMonth(monthly);
        record.setDay(daily);
        // 打卡类型 ; 0-无效打卡,1-上班打卡,2-下班打卡,3-完成打卡
        record.setPunchType(punchCheck.getWorkStatus());
        // 标准时间
//		record.setStandardTime(standardTime);
        // 打卡时间
//		record.setPunchTime(punchTime);
        // 打卡状态
        record.setPunchStatus(punchCheck.getPunchStatus());
        // 迟到/早退分钟数
        record.setMinute(punchCheck.getMinutes());
        // 打卡坐标
        record.setCoordinate(attendPunchDTO.getLongitude() + "," + attendPunchDTO.getLatitude());
        // 打卡地点
        record.setLocation(punchCheck.getPunchLocation());
        // 打卡wifi
        record.setPunchWifi(attendPunchDTO.getWifiName() + "," + attendPunchDTO.getMacAddr());
        // 打卡来源 ; 1-app打卡,2-考勤机打卡
        record.setPunchSource(1);
        // 是否补卡 ; 1-是,0-否
        record.setIsMakeUp(false);
        record.setPunchSerial(punchCheck.getPunchSerial());

        AttendPunchRecordVO attendPunchRecordVO = attendPunchRecordService.save(record, tokenUser);

        if (workStatus != 3) {

            // 考勤打卡日统计
            this.summaryAttendPunchDaily(punchCheck, empInfoDO, punchTime, tokenUser);

            //考勤人员月统计
            attendPeopleMonthlyStatService.monthlyStat(attendPunchRecordVO, punchCheck.getPeriod(), tokenUser);

            // 统计加班
            if (StringUtils.isNotBlank(punchCheck.getEndOffWorkTime())) {

                if (punchCheck.getEndOffWorkTime().equals(punchCheck.getOffWorkTime())) {

                    AutoCalculOverTimeDTO autoCalculOverTimeDTO = new AutoCalculOverTimeDTO();
                    autoCalculOverTimeDTO.setAttendPlanId(punchCheck.getAttendPlanId());
                    autoCalculOverTimeDTO.setEmpInfoId(empInfoDO.getId());
                    autoCalculOverTimeDTO.setOffWorkTime(punchCheck.getEndOffWorkTime());
                    autoCalculOverTimeDTO.setPunchTime(punchCheck.getOffWorkPunchTime());
                    autoCalculOverTimeDTO.setYearly(yearly);
                    autoCalculOverTimeDTO.setMonthly(monthly);
                    autoCalculOverTimeDTO.setDaily(daily);
                    // 日期类型 1-工作日,2-休息日,3-法定节假日
                    int dateType = 0;
                    // 今日是否需要考勤;true-是,false-否
                    if (punchCheck.getIsTodayAttend()) {
                        dateType = 1;
                    } else {
                        dateType = 2;
                    }
                    // 特殊日期是否需要打卡;true-是,false-否
                    if (punchCheck.getIsNeedPunchOfDaily()) {
                        dateType = 1;
                    } else {
                        dateType = 2;
                    }
                    autoCalculOverTimeDTO.setDateType(dateType);
                    attendOvertimeRuleService.autoCalculOverTime(autoCalculOverTimeDTO, tokenUser);
                }
            }
        }

        attendPunchVO = punchCheck.to(AttendPunchVO.class);

        return attendPunchVO;
    }


    /**
     * @param empInfoId
     * @param siteId
     * @return
     */
    private DepInfoDO getDepInfo(String empInfoId, String siteId) {

        DepInfoDO depInfoDO = null;

        EmpDepQuery empDepQuery = new EmpDepQuery();
        empDepQuery.setEmpId(empInfoId);
        List<EmpDepDO> empDepList = empDepService.selectDO(empDepQuery, siteId);
        if (empDepList.size() <= 0) {
            return depInfoDO;
        }

        // 查询部门信息
        depInfoDO = depInfoService.selectDOById(empDepList.get(0).getDepId(), siteId);

        return depInfoDO;
    }
}
